package com.example.servicegateway.route.rule;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.exception.NacosException;
import com.example.servicegateway.route.rule.domain.RouteDomain;
import lombok.extern.slf4j.Slf4j;
import ognl.Ognl;
import ognl.OgnlException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 新服务上线，动态添加gateway路由配置
 *
 * 通过监听器传来的服务名列表，与routes的id匹配，服务名在routes不存在则添加路由规则
 * @author K
 */
@Slf4j
@Component
public class AutoAddRouter {

    @Value("${spring.application.name}")
    private String applicateName;
    @Value("${spring.profiles.active:}")
    private String active;

    private final NacosConfigManager nacosConfigManager;

    public AutoAddRouter(NacosConfigManager nacosConfigManager) {
        log.info("AutoAddRouterConfig 初始化");
        this.nacosConfigManager = nacosConfigManager;
    }

    /**
     * 添加路由规则
     * @param serviceNames 服务名列表
     * @throws Exception 异常
     */
    public void addRouterRule(List<String> serviceNames ) throws Exception {
        // 加载yaml文件并转换Map对象
        Map yamlMap = getYaml();
        if (yamlMap.isEmpty()) {
            return;
        }

        // 获取配置文件spring.cloud.gateway.routes下的参数配置
        List<RouteDomain> routeDomains = getRouteDomains(yamlMap);
        log.info("routeDomains = " + JSON.toJSONString(routeDomains));

        // 新路由规则列表（包含旧路由规则）
        List<RouteDomain> newRouteDomains = genRoute(routeDomains, serviceNames);
        if (newRouteDomains.isEmpty()) {
            log.debug("没有新增规则");
            return;
        }
        log.info("新规则：" + JSON.toJSONString(newRouteDomains));
        routeDomains.addAll(newRouteDomains);

        // 转换后将新的路由列表替换旧的列表
        String newRouteDomainsJson = JSON.toJSONString(routeDomains);
        List<LinkedHashMap<String, Object>> linkedHashMap = JSON.parseObject(newRouteDomainsJson,
                new TypeReference<>() {});
        Ognl.setValue("spring.cloud.gateway.routes", yamlMap, linkedHashMap);
        // 新路由配置同步到nacos
        pushConfig(yamlMap);
    }

    private Map getYaml() throws NacosException {
        ConfigService configService = nacosConfigManager.getConfigService();
        String config = configService.getConfig(getDataId(), getGroupName(), 3000);
        log.debug("原gateway配置 = " + config);
        if (StringUtils.isBlank(config)) {
            return new HashMap(1);
        }

        Yaml yaml = new Yaml();
        return yaml.loadAs(config, LinkedHashMap.class);
    }

    private List<RouteDomain> getRouteDomains(Map yamlMap) throws OgnlException {
        List routes = (List) Ognl.getValue("spring.cloud.gateway.routes", yamlMap);

        String routesJson = JSON.toJSONString(routes);
        return JSON.parseObject(routesJson, new TypeReference<>() {});
    }

    private List<RouteDomain> genRoute(List<RouteDomain> routeDomains, List<String> serviceNames) throws URISyntaxException {
        List<String> validServiceNames = getValidServiceNames(serviceNames);
        return genRouteDomans(validServiceNames, routeDomains);
    }

    private List<String> getValidServiceNames(List<String> serviceNames) {
        serviceNames.removeIf(serviceName -> StringUtils.equalsIgnoreCase(applicateName, serviceName));
        return serviceNames;
    }

    private List<RouteDomain> genRouteDomans(List<String> serviceNames, List<RouteDomain> routeDomains) throws URISyntaxException {
        Map<String, RouteDomain> map = routeDomains.stream()
                .collect(Collectors.toMap(RouteDomain::getId, Function.identity(), (key1, key2) -> key1));

        List<RouteDomain> list = new ArrayList<>();
        for (String serviceName : serviceNames) {
            if (map.containsKey(serviceName)) {
                continue;
            }

            RouteDomain routeDomain = new RouteDomain();
            routeDomain.setId(serviceName);
            routeDomain.setUri(new URI("lb://" + serviceName));
            routeDomain.setFilters(List.of("StripPrefix=1"));
            routeDomain.setPredicates(getPredicates(serviceName));

            list.add(routeDomain);
        }
        return list;
    }

    private List<String> getPredicates(String serviceName) {
        String[] strArray = serviceName.split("-");
        String bizName = strArray[0] + "-biz-" + strArray[1];
        String fullName = "Path=/" + bizName + "/**";
        return List.of(fullName);
    }

    public void pushConfig(Map yamlMap) throws NacosException {
        Yaml yaml = new Yaml();
        String config = yaml.dumpAsMap(yamlMap);
        log.info("新gateway配置 = " + config);

        ConfigService configService = nacosConfigManager.getConfigService();
        configService.publishConfig(getDataId(), getGroupName(), config, ConfigType.YAML.getType());
    }

    private String getGroupName() {
        NacosConfigProperties nacosConfigProperties = nacosConfigManager.getNacosConfigProperties();
        return nacosConfigProperties.getGroup();
    }

    private String getDataId() {
        NacosConfigProperties nacosConfigProperties = nacosConfigManager.getNacosConfigProperties();
        String active = StringUtils.isNotBlank(this.active) ? "-" + this.active : "";
        return applicateName + active + "." + nacosConfigProperties.getFileExtension();
    }
}
